home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / bit / src / ps.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  48KB  |  1,779 lines

  1. /*
  2.  * $Id: ps.c,v 0.91 1994/02/23 02:26:57 zhao Pre-Release $
  3.  *
  4.  *. This file is part of BIT shareware package. After the two weeks of
  5.  *  free evaluation period, you are encouraged (required) to register
  6.  *  your copy for a small registration fee, which is $35 for personal use
  7.  *  and $50 for commercial, government and institutional use.
  8.  *
  9.  *  Copyright(c) 1993, 1994 by T.C. Zhao.
  10.  *  All rights reserved.
  11.  *
  12.  *  Permission to use, copy, and distribute this software in its entirety
  13.  *  for non-commercial purposes is hereby granted, provided that the
  14.  *  above shareware and copyright notices and this permission notice
  15.  *  appear in all copies and their documentation.
  16.  *
  17.  *  This software may be modified for your own use, but modified versions
  18.  *  may not be distributed without prior consent of the author.
  19.  *
  20.  *  This software is provided "as is" without expressed or implied
  21.  *  warranty of any kind.
  22.  *
  23.  *.
  24.  *
  25.  * PostScript support:
  26.  *
  27.  *   Rasterization is done via psrender or ghostscript at configurable
  28.  *   resolutions. In case of multi-page EPS files, multi-image handler
  29.  *   is called to allow random access to each page.
  30.  *
  31.  *   On output, bit produces a parameterized color EPS file unless of
  32.  *   course the input image is grayscale. Currently, no consideration is
  33.  *   given to text and sgfs as far as checking if their are off page although
  34.  *   their contribution to the overall size and bounding box is considered.
  35.  *   Maybe a flag to request to center the whole bounding box rather than
  36.  *   centering the raster only as current implementation does.
  37.  *
  38.  * Warning: If PCBITS is not 8, need to renormalize the primary color
  39.  * before doing any thing. This is unchecked.
  40.  *
  41.  * BUGS: always generates 8bits grapscale even if B&W images
  42.  */
  43. #ifndef NO_PS
  44. #if !defined(lint) && defined(F_ID)
  45. char *id_ps = "$Id: ps.c,v 0.91 1994/02/23 02:26:57 zhao Pre-Release $";
  46. #endif
  47.  
  48. #include "bit.h"
  49. #include "extern.h"
  50. #include "tsdef.h"
  51. #include <math.h>        /* for log10 */
  52.  
  53. /*******************************************************************
  54.  * PostScript loading is supported either thru psrender or ghostscript
  55.  ****************************************************************{**/
  56.  
  57. /********************************************************************
  58.  * resolutions to use when converting PS to raster image.
  59.  * Gloabl variable ps_res_index is initialized to zero and used
  60.  * as an index into ps_res table.
  61.  * gps_res_string returns the string representtion of resolutions
  62.  ********************************************************************/
  63.  
  64. static int ps_res[] =
  65. {
  66.     96, 150, 300, 400, 35, 72
  67. };
  68.  
  69. const char *
  70. gps_res_string(void)
  71. {
  72.     return "96dpi|150dpi|300dpi|400dpi|35dpi|72dpi";
  73. }
  74.  
  75. int
  76. PS_desc(IPTR im)
  77. {
  78.     im->w = im->h = 1;
  79.     im->type = T_RGBA;
  80.     return 0;
  81. }
  82.  
  83. /******************************************************************
  84.  * Read a PS file via either ghostscript or psrender
  85.  ******************************************************************/
  86. #define GS_DEVICE   "ppmraw"
  87. #define GS_OPT      "-q -DNOPAUSE"
  88.  
  89. static int npage, cur_page;    /* no. of pages in EPS file  */
  90. static int isgs = 1;        /* ghostscript ok            */
  91. static char psprefix[MAXDLEN];    /* prefix for converted files */
  92. static char pspat[MAXDLEN];    /* psprefix + *               */
  93.  
  94. static FL_FORM *multipage;
  95. static void create_form_multipage(void);
  96.  
  97. /**************************************************************
  98.  * Load rasterized page, cur_page.
  99.  * Page is stored in a file whose name is generated on the fly
  100.  * depending which/what interpreter is used.
  101.  * Return -1 for failure
  102.  **************************************************************/
  103. static int
  104. load_page(IPTR im)
  105. {
  106.     static char fname[1024];
  107.     int badload;
  108.  
  109.     if (isgs)
  110.     sprintf(fname, "%s%d", psprefix, cur_page);
  111.     else
  112.     sprintf(fname, "%s%0*d.rgb", psprefix,
  113.         (int) (log10(npage) + 1.1), cur_page);
  114.  
  115.     M_info("PS_Load", "trying %s", fname);
  116.  
  117.     /* block paging request while loading */
  118.     fl_deactivate_form(multipage);
  119.  
  120.     close_image(im);
  121.  
  122.     badload = !(im->fp = fopen(get_TMPfile(fname), "r")) ||
  123.     (PNM_desc(im) < 0 || img_get_rastermem(im) < 0);
  124.  
  125.     badload = badload ||
  126.     (((IS_CI(im) ? PBM_load : PPM_load) (im) >= 0) ? 0 : -1);
  127.  
  128.     M_info("PS_load", "Loading %sOK", badload ? "Not " : "");
  129.  
  130.     /* check if black & white */
  131.     if (!badload && IS_CI(im) && preserve_wm_colors)
  132.     img_preserve_wm_colors(im);
  133.  
  134.     M_info("PS_load", "Preserving OK");
  135.  
  136.     fl_activate_form(multipage);
  137.  
  138.     /*
  139.      * for multi-page EPS file, multi handler will clean after itself. remove
  140.      * file only if single page PS file
  141.      */
  142.     if (npage == 1)
  143.     remove(get_TMPfile(fname));
  144.  
  145.     return badload ? -1 : 0;
  146. }
  147. /*****************************************************************
  148.  * Call external interpretor to render the (e)PS files. All rendered
  149.  * pages are written to disk. This routine only load the first page
  150.  ******************************************************************/
  151. int
  152. PS_load(IPTR im)
  153. {
  154.     char shcmd[1024];
  155.     int np, psres = ps_res[ps_res_index], status;
  156.     static int ui;
  157.     Dirlist *dl, *dls;
  158.  
  159.     /* get the tail of filename */
  160.     strcpy(pspat, file_tail(im->ifile));
  161.  
  162.     /* and generate a uniq pattern out of it for output files */
  163.     sprintf(psprefix, "%s_%d%s", pspat, ui++, "_eps");
  164.     strcat(strcpy(pspat, psprefix), "*");
  165.  
  166.     M_info("PS_load", "psprefix=%s pspat=%s", psprefix, pspat);
  167.  
  168.     /* always try gs first: smarter than psrender */
  169.     show_busy("Reading PS ...");
  170.  
  171.     sprintf(shcmd, "gs -sDEVICE=%s %s -r%d -sOutputFile=%s%%d -- %s",
  172.         GS_DEVICE, GS_OPT, psres, get_TMPfile(psprefix), im->ifile);
  173.  
  174.     M_info("PSLoad", "%s", shcmd);
  175.  
  176.     isgs = 1;
  177.  
  178.     if ((status = system(shcmd)))
  179.       {
  180.       isgs = 0;
  181.       M_info("PS_load", "gs failed with error code %d", status);
  182.       sprintf(shcmd, "psrender -d image -r %d -o %s %s",
  183.           psres, get_TMPfile(psprefix), im->ifile);
  184.       M_info("PSLoad", "%s", shcmd);
  185.       status = system(shcmd);
  186.       }
  187.  
  188.  
  189.     if (status)
  190.       {
  191.       Bark("PS_load", "Both gs and psrender failed");
  192.       return -1;
  193.       }
  194.  
  195.     create_form_multipage();
  196.  
  197.  
  198.     /* succesful: see how many pages we've got */
  199.     if (!(dl = get_dir_list(tmppath, pspat, &np, 1)))
  200.     return -1;
  201.  
  202.     M_info("PS_load", "total of %d files", np);
  203.  
  204.     /* dirlist always contain dirs, remove it from dirlist. */
  205.     for (dls = dl + np; dl < dls; dl++)
  206.       {
  207.       if (dl->type == FT_DIR)
  208.           --np;
  209.       }
  210.  
  211.     /* this is the true no. of pages */
  212.     if ((npage = np) <= 0)
  213.     return -1;
  214.  
  215.     M_info("PS_load", "total of %d pages", npage);
  216.  
  217.     cur_page = 1;
  218.  
  219.     /* if more than one page, say so to the driver */
  220.     im->more = npage > 1;
  221.  
  222.     status = load_page(im);
  223.  
  224.     M_info("LoadPage", "status=%d", status);
  225.  
  226.     /* load the first page and return */
  227.     return status >= 0 ? 0 : -1;
  228. }
  229.  
  230. /**** Handle multi-page EPS files **/
  231. static FL_OBJECT *flpagecnt;
  232.  
  233. static void
  234. show_page_no(IPTR im)
  235. {
  236.     char ha[100];
  237.     sprintf(ha, "EPS Page %d of %d", cur_page, npage);
  238.     set_iformat_info(im, ha);
  239.     update_image_info(im);
  240. }
  241.  
  242. int
  243. PS_next(IPTR im, int n)
  244. {
  245.     short val;
  246.     int oreport = report_level;
  247.  
  248.     report_level = 0;        /* supress progress report */
  249.  
  250.     create_form_multipage();
  251.     show_page_no(im);
  252.  
  253.     fl_set_slider_bounds(flpagecnt, 1, npage);
  254.     fl_set_slider_value(flpagecnt, 1);
  255.  
  256.     bit_show_form(multipage, FL_PLACE_MOUSE, 0, "EPS_PAGE");
  257.     while (bit_qread(&val) != F1KEY || !val)
  258.     ;
  259.     bit_hide_form(multipage);
  260.     report_level = oreport;
  261.  
  262.     /* clean up all files */
  263.     rm_all_files(tmppath, pspat);
  264.  
  265.     im->more = 0;
  266.     return 0;
  267. }
  268.  
  269. /***** Display a particular page, cur_page ****/
  270. static void
  271. show_page(IPTR im)
  272. {
  273.     show_page_no(im);
  274.     /* load & display  */
  275.     im->ok = load_page(im) >= 0;
  276.     im->io->display(im, display_style, 0);
  277. }
  278.  
  279. /* ARGSUSED */
  280. static void
  281. ps_random_page(FL_OBJECT * ob, long q)
  282. {
  283.     int n = (fl_get_slider_value(ob) + 0.1);
  284.  
  285.     if (cur_page != n)
  286.       {
  287.       cur_page = n;
  288.       show_page(imgptr);
  289.       }
  290. }
  291.  
  292. /* ARGSUSED */
  293. static void
  294. ps_next_prev_page(FL_OBJECT * ob, long q)
  295. {
  296.     int n = cur_page + q;
  297.  
  298.     n = (n < 1 ? 1 : (n > npage ? npage : n));
  299.  
  300.     if (cur_page != n)
  301.       {
  302.       cur_page = n;
  303.       fl_set_slider_value(flpagecnt, cur_page);
  304.       show_page(imgptr);
  305.       }
  306. }
  307.  
  308. /* ARGSUSED */
  309. static void
  310. ps_page_done(FL_OBJECT * ob, long q)
  311. {
  312.     fl_qenter(F1KEY, 100);
  313. }
  314.  
  315. static void
  316. create_form_multipage(void)
  317. {
  318.     FL_OBJECT *obj;
  319.  
  320.     if (multipage)
  321.     return;
  322.  
  323.     multipage = fl_bgn_form(FL_NO_BOX, 200.0, 205.0);
  324.     obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 200.0, 205.0, "");
  325.     fl_set_object_color(obj, 12, 47);
  326.     obj = fl_add_box(FL_DOWN_BOX, 10.0, 10.0, 180.0, 185.0, "");
  327.     fl_set_object_color(obj, 9, 47);
  328.     obj = fl_add_text(FL_NT, 30.0, 160.0, 140.0, 25.0, "MultiPage Image");
  329.     fl_set_object_lcol(obj, 4);
  330.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  331.  
  332.     obj = fl_add_button(FL_NB, 30.0, 70.0, 70.0, 25.0, "PrevPage");
  333.     fl_set_object_color(obj, 47, 9);
  334.     fl_set_object_lsize(obj, 10.0);
  335.     fl_set_call_back(obj, ps_next_prev_page, -1);
  336.  
  337.     obj = fl_add_button(FL_NB, 100.0, 70.0, 70.0, 25.0, "NextPage");
  338.     fl_set_object_color(obj, 47, 9);
  339.     fl_set_object_lsize(obj, 10.0);
  340.     fl_set_call_back(obj, ps_next_prev_page, 1);
  341.  
  342.     flpagecnt = obj = fl_add_valslider(FL_HNS, 20.0, 110.0, 160.0, 25.0,
  343.                        "Current EPS Page");
  344.     fl_set_object_color(obj, 9, 1);
  345.     fl_set_object_lsize(obj, 10.0);
  346.     fl_set_object_align(obj, FL_ALIGN_TOP);
  347.  
  348.     fl_set_slider_return(obj, 0);
  349.     fl_set_slider_precision(obj, 0);
  350.     fl_set_slider_step(obj, 1);
  351.     fl_set_call_back(obj, ps_random_page, 0);
  352.  
  353.  
  354.     obj = fl_add_button(FL_NB, 115.0, 20.0, 60.0, 25.0, "Done");
  355.     fl_set_object_color(obj, 47, 9);
  356.     fl_set_object_lsize(obj, 10.0);
  357.     fl_set_call_back(obj, ps_page_done, 0);
  358.     fl_end_form();
  359. }
  360.  
  361. /*****************************************************************
  362.  *         END of reading                                        *
  363.  **************************************************************}**/
  364.  
  365. /*****************************************************************
  366.  * EPS output
  367.  *****************************************************************/
  368.  
  369. /******* Defines and limits. Some modifiable at run time *****/
  370. #define PS_LINE       36    /* 72 characters per line */
  371. #define PRINTER_DPI   300    /* default printer res.   */
  372. #define PG_WIDTH      8.5    /* page width in inches   */
  373. #define PG_HEIGHT     11.0    /* page height in inches  */
  374. #define MARGIN        0.4    /* margin, about 1cm      */
  375. #define MINTEXTSIZE   4.0    /* minimum font size */
  376.  
  377. /************ Output related variables *********************/
  378.  
  379. static float s2p;        /* factor from screen size to print size */
  380. static float gxscale = 1.0;    /* X scale, further modified by s2p      */
  381. static float gyscale = 1.0;    /* Y scale, further modified by s2p      */
  382. static int autoscale = 1;    /* true to auto scale(down)              */
  383. static int matchscreen = 1;    /* if make hardcopy same size as on scrn */
  384. static int landscape;        /* the actual placement                  */
  385. static int textscale;        /* if scale applies to text              */
  386. static float xo, yo;        /* offset into page                      */
  387.  
  388. static float page_h = PG_HEIGHT;/* Default paper width          */
  389. static float page_w = PG_WIDTH;    /* Default paper height         */
  390. static float hm = MARGIN;    /* horizotal margin             */
  391. static float vm = MARGIN;    /* vertical margin              */
  392. static int pdpi = PRINTER_DPI;    /* printer resolution */
  393. static int screen_dpi;        /* screen resolution */
  394. static int misct, miscl, miscb, miscr;    /* text and sgf sizes */
  395. static int psnewcolor, psnewfont;    /* initialization flag */
  396.  
  397. static FL_FORM *fmpsinit;
  398. static void get_summary(void);
  399. extern void get_misc_size(IPTR, int *, int *, int *, int *);
  400. static void create_form_psinit(void);
  401.  
  402. /********************************************************
  403.  * Generate current output parameters in string format
  404.  ********************************************************/
  405.  
  406. /* placement: -1: auto 0: portrait 1: landscape */
  407. static int place = -1;
  408. static const char *rplace[] =
  409. {
  410.     "Autofit", "Portrait", "Landscape"
  411. };
  412.  
  413.  /* ARGSUSED */
  414. const char *
  415. PS_wdefault(const IPTR im)
  416. {
  417.     static char qtmp[100];
  418.     const char *p = rplace[place + 1];
  419.  
  420.     if (autoscale)
  421.     sprintf(qtmp, "Autoscale %s", p);
  422.     else
  423.     sprintf(qtmp, "Xs=%g Ys=%g %s", gxscale, gyscale, p);
  424.     return qtmp;
  425. }
  426.  
  427.  
  428. /*************************************************************
  429.  * Get options interactively. Called by driver
  430.  ***************************************************************/
  431. int
  432. PSdump_init(IPTR im)
  433. {
  434.     create_form_psinit();
  435.     screen_dpi = get_scr_dpi();
  436.     get_misc_size(im, &misct, &miscl, &miscb, &miscr);
  437.     get_summary();
  438.     fl_deactivate_all_forms();
  439.     bit_show_form(fmpsinit, FL_PLACE_MOUSE, 0, "PSoption");
  440.     /* will be reactivated by cancel or ok */
  441.     return 0;
  442. }
  443.  
  444.  
  445. static void
  446. PS_push(FILE * fp)
  447. {
  448.     fputs("gsave\n", fp);
  449. }
  450.  
  451. static void
  452. PS_pop(FILE * fp)
  453. {
  454.     fputs("grestore\n", fp);
  455. }
  456.  
  457. static void
  458. PS_move(FILE * fp, int x, int y)
  459. {
  460.     fprintf(fp, "%d %d moveto\n", x, y);
  461. }
  462.  
  463. static char pscmd[100];
  464.  
  465.  
  466. /********************************************************************
  467.  * Write conforming PS header and comments. default user coordinates
  468.  * system is used throughout, whose units incidently are almost the same
  469.  * as a pixel-pixel distance on screen, and we can use the pixel as the
  470.  * unit and get a hardcopy that is approximately the same as they appear
  471.  * on screen
  472.  ***********************************************************************/
  473.  
  474. /* special characters in PS */
  475. #define PS_SPC(c) ( (c)=='(' || (c)==')' || (c)=='[' || (c)==']' || \
  476.                     (c)=='<' || (c)=='>' || (c)=='%' || (c)=='\\' || (c)=='#')
  477.  
  478. /* Maximum number of supported SGF's */
  479.  
  480. #define MAXOBJDEFS 20
  481.  
  482. /*************************************************************
  483.  * Find the optimum scale factors such that the scaled image
  484.  * (just) fit the printed page (minus the margins)
  485.  *************************************************************/
  486. static int
  487. auto_scale(float pgw, float pgh, float w, float h)
  488. {
  489.     float rescalex, rescaley;
  490.     int i = 0;
  491.  
  492.     if ((!landscape && (pgw < w || pgh < h)) ||
  493.     (landscape && (pgw < h || pgh < w)))
  494.       {
  495.       if (!landscape)
  496.         {
  497.         rescalex = (pgw - 2.0) / w;
  498.         rescaley = (pgh - 2.0) / h;
  499.         }
  500.       else
  501.         {
  502.         rescalex = (pgw - 2.0) / h;
  503.         rescaley = (pgh - 2.0) / w;
  504.         }
  505.       i = (100.0 * Min(rescalex, rescaley));
  506.       }
  507.     return i;
  508. }
  509.  
  510. /*************************************************************
  511.  * Find the optimum orientation
  512.  **************************************************************/
  513. static void
  514. auto_fit(float pgw, float pgh, float w, float h)
  515. {
  516.     int plm, pbm, llm, lbm;
  517.  
  518.     plm = (pgw - w) / 2;
  519.     pbm = (pgh - h) / 2;
  520.     llm = (pgw - h) / 2;
  521.     lbm = (pgh - w) / 2;
  522.     landscape = (Abs(lbm - llm) < Abs(pbm - plm));
  523. }
  524.  
  525. /******************************************************************
  526.  * check if scaling is needed to fit the page. All units are
  527.  * in Ps units, i.e., 72 units per inch.
  528.  ******************************************************************/
  529. static void
  530. check_size(int *ww, int *hh, int fit)
  531. {
  532.     float pgw = page_w - 2.0 * hm;    /* writable area */
  533.     float pgh = page_h - 2.0 * vm;    /* writable area */
  534.     float rescale;
  535.     int w = *ww, h = *hh;
  536.     float hhm, vvm;
  537.     int i, n;
  538.  
  539.  
  540.     pgw *= 72.0;
  541.     pgh *= 72.0;
  542.  
  543.     hhm = hm * 72.0;
  544.     vvm = vm * 72.0;
  545.  
  546.     if (autoscale)
  547.     gxscale = gyscale = 1.0;
  548.  
  549.     s2p = matchscreen ? (72.0 / screen_dpi) : 1.0;
  550.     w *= gxscale * s2p;
  551.     h *= gyscale * s2p;
  552.  
  553.     /* make sure each unit has integral no. of printer dots */
  554.     if ((n = (pdpi / 72.0 + 0.5)) < 1)
  555.     n = 1;
  556.  
  557.     w = (w * 72.0 / pdpi) * n;
  558.     h = (h * 72.0 / pdpi) * n;
  559.  
  560.     /* file maybe extremely small or printer very expensive! */
  561.  
  562.     if (w < 1 || h < 1)
  563.       {
  564.       w = (w * 72.0 * n / pdpi);
  565.       h = (h * 72.0 * n / pdpi);
  566.       }
  567.  
  568.     /* true size should be scaled to match the screen size */
  569.  
  570.     /* find the best placement if auto */
  571.  
  572.     if ((landscape = place) == -1)
  573.     auto_fit(pgw, pgh, w, h);
  574.  
  575.     /* check if image will fit on a page only if requested */
  576.  
  577.     if (fit || autoscale)
  578.       {
  579.       i = auto_scale(pgw, pgh, w, h);
  580.       if ((rescale = 0.01 * i) < 0.005)
  581.           rescale = 1.0;
  582.  
  583.       if (i >= 2 && !autoscale)
  584.         {
  585.         sprintf(pscmd, "rescale (by %.2f) ?", rescale);
  586.         if (!yes_no("Warning", "Image too large to fit", pscmd, 0))
  587.             rescale = 1.0;
  588.         }
  589.  
  590.       gxscale *= rescale;
  591.       gyscale *= rescale;
  592.  
  593.       w *= rescale;
  594.       h *= rescale;
  595.       }
  596.  
  597.  
  598.     /*
  599.      * get the origin of the system, for BoundingBox only. Actual PS file
  600.      * will do this calculation on the fly for possible further editing.
  601.      */
  602.  
  603.     xo = hhm + (pgw - (landscape ? h : w)) * 0.5;
  604.     yo = vvm + (pgh - (landscape ? w : h)) * 0.5;
  605.  
  606.  
  607.     xo -= (landscape ? miscb : miscl) * gxscale * s2p;
  608.     yo -= (landscape ? miscl : miscb) * gyscale * s2p;
  609.  
  610.     *ww = w;
  611.     *hh = h;
  612. }
  613.  
  614.  
  615. /**************************************************************
  616.  * actual header emitting routine
  617.  ****************************************************************/
  618. static void PS_text_init(FILE *);
  619. static void PS_sgf_init(FILE *);
  620.  
  621. static void
  622. PS_header(IPTR im)
  623. {
  624.     FILE *fp = im->fp;
  625.     int rgb = IS_RGBA(im);
  626.     int w = im->w, h = im->h;
  627.     char *psc;
  628.     const char *bdef = "bind def";
  629.  
  630.     check_size(&w, &h, 1);
  631.  
  632.     w += (miscr + miscl) * gxscale * s2p;
  633.     h += (misct + miscb) * gyscale * s2p;
  634.  
  635.     /* Write a conforming header */
  636.  
  637.     fputs("%!PS-Adobe-3.0 EPSF-2.0\n", fp);
  638.     fprintf(fp, "%%%%Creator: %s (C) 1993, T.C. Zhao\n", rm_rcs_kw(sver));
  639.     fprintf(fp, "%%%%Title: %s(%g X %g)\n",
  640.         im->ifile, gxscale * s2p, gyscale * s2p);
  641.  
  642.     fprintf(fp, "%%%%CreateDate: %s\n", current_ascii_date());
  643.  
  644.     /* we enlarge the bounding box by 2 units for possible thick lines */
  645.     fprintf(fp, "%%%%BoundingBox: %d %d %d %d\n",
  646.         (int) (xo - 1), (int) (yo - 1),
  647.         (int) (xo + w + 1.5), (int) (yo + h + 1.5));
  648.  
  649.     fprintf(fp, "%%%%Pages: 1\n");
  650.     if (number_of_text() > 0)
  651.       {
  652.       fprintf(fp, "%%%%DocumentFonts: %s\n", get_text_fontname(0));
  653.       }
  654.     fputs("%%EndComments\n", fp);
  655.  
  656.     /* Prolog. Basic defines. */
  657.     fputs("/inch { 72 mul } def \n", fp);
  658.     fputs("\n% Beginning of modifiable parameters\n", fp);
  659.     fprintf(fp, "/pagew {%.2g inch} %s \t%% page width\n", page_w, bdef);
  660.     fprintf(fp, "/pageh {%.2g inch} %s \t%% page height\n", page_h, bdef);
  661.     fprintf(fp, "/lm {%.2g inch} %s \t%% Left margin\n", hm, bdef);
  662.     fprintf(fp, "/bm {%.2g inch} %s \t%% Bottom margin\n", vm, bdef);
  663.     fprintf(fp, "/xscale %g  def  \t%% global xscale\n", s2p * gxscale);
  664.     fprintf(fp, "/yscale %g  def  \t%% global yscale\n", s2p * gxscale);
  665.     fputs("% End of modifiable parameters\n\n", fp);
  666.  
  667.     fprintf(fp, "/pw {pagew lm 2 mul sub} %s \t%% writable width\n", bdef);
  668.     fprintf(fp, "/ph {pageh bm 2 mul sub} %s \t%% writable height\n", bdef);
  669.  
  670.     /*
  671.      * instead of emitting numbers, the actual calculation is output so that
  672.      * both scale and margin can be adjusted in PS
  673.      */
  674.     fprintf(fp, "/xo pw xscale div %d sub 2 div def \t%% xo\n",
  675.         landscape ? im->h : im->w);
  676.     fprintf(fp, "/yo ph yscale div %d sub 2 div def \t%% yo\n",
  677.         landscape ? im->w : im->h);
  678.  
  679.     PS_text_init(fp);
  680.     PS_sgf_init(fp);
  681.  
  682.  
  683.     sprintf(pscmd, "StartRasterOf%s", im->ifile);
  684.  
  685.     /* replace special characters with $ */
  686.     psc = pscmd;
  687.     while (*psc++)
  688.     if (PS_SPC(*psc))
  689.         *psc = '$';
  690.  
  691.     /*
  692.      * there is a bug in PSVIEW(IRIX 3.3) that the pagematrix is not honored.
  693.      * Have to code the default, i.e., from ToptoBottom for preview
  694.      */
  695.  
  696.     if (!rgb)
  697.       {
  698.       fprintf(fp, "/graystring %d string def\n", im->w);
  699.       fprintf(fp, "/%s\n", pscmd);
  700.       fprintf(fp, "  {%d %d %d [ %d 0 0 -%d 0 %d]\n",
  701.           im->w, im->h, (int) PCBITS, im->w, im->h, im->h);
  702.       fputs("  {currentfile graystring readhexstring pop}\n", fp);
  703.       fputs("  image\n} bind def\n", fp);
  704.       }
  705.     else
  706.       {
  707.       fprintf(fp, "/redstring %d string def\n", im->w);
  708.       fprintf(fp, "/grnstring %d string def\n", im->w);
  709.       fprintf(fp, "/blustring %d string def\n", im->w);
  710.       fprintf(fp, "/%s\n", pscmd);
  711.       fprintf(fp, "  {%d %d %d [ %d 0 0 -%d 0 %d]\n",
  712.           im->w, im->h, (int) PCBITS, im->w, im->h, im->h);
  713.       fputs("  {currentfile redstring readhexstring pop}\n", fp);
  714.       fputs("  {currentfile grnstring readhexstring pop}\n", fp);
  715.       fputs("  {currentfile blustring readhexstring pop}\n", fp);
  716.       fputs("  true 3 colorimage\n} bind def\n", fp);
  717.       }
  718.     fputs("%%EndProlog\n%%Page: 1 1\n", fp);
  719.  
  720.     /* honor the margin request */
  721.     PS_push(fp);
  722.     fputs("lm bm translate  % Margins\n", fp);
  723. }
  724.  
  725. static void
  726. PS_trailer(FILE * fp)
  727. {
  728.     PS_pop(fp);
  729.     fputs("showpage\n", fp);
  730.     fputs("%%Trailer\n", fp);
  731. }
  732.  
  733. static void
  734. PS_rotate(IPTR im)
  735. {
  736.     if (landscape)
  737.       {
  738.       fprintf(im->fp, "%d %d translate\n", im->h, 0);
  739.       fputs(" 90 rotate\n", im->fp);
  740.       }
  741. }
  742.  
  743. static void
  744. PS_translate(IPTR im)
  745. {
  746.     fputs("xo yo translate\n", im->fp);
  747. }
  748.  
  749. static void
  750. PS_scale(IPTR im)
  751. {
  752.     fputs("xscale yscale scale\n", im->fp);
  753. }
  754.  
  755. static void
  756. PS_ras_scale(IPTR im)
  757. {
  758.     fprintf(im->fp, " %d %d scale\n", im->w, im->h);
  759. }
  760.  
  761. /* scale and rotate */
  762. static void
  763. PS_map(IPTR im)
  764. {
  765.     PS_scale(im);
  766.     PS_translate(im);
  767.     PS_rotate(im);
  768. }
  769.  
  770. /********************************************************************
  771.  * Text related output routines: the source of text attributes
  772.  * are defined by text routines in deferred mode.
  773.  ****************************************************************{*/
  774.  
  775. /*************************************************************
  776.  * Switch to ISOLatin1 to get some of the special characters.
  777.  * If the printer is level 1 only, switching code has not
  778.  * effect.
  779.  *************************************************************/
  780.  
  781. static void
  782. switch_to_ISOLatin1(FILE * fp)
  783. {
  784.     /***** usage: newfontname oldfontname SwitchISO ******/
  785.  
  786.     fputs("\n% Switch to ISOLatin1 to get special characters\n", fp);
  787.     fputs("/languagelevel where\n", fp);
  788.     fputs("   {pop languagelevel} {1} ifelse\n", fp);
  789.     fputs("   2 lt {/BitEncoding /StandardEncoding load def}\n", fp);
  790.     fputs("        {/BitEncoding /ISOLatin1Encoding load def}\n", fp);
  791.     fputs("   ifelse\n", fp);
  792.     fputs("/SwitchToISO {\n", fp);
  793.     fputs("   findfont dup length\n   dict begin {1 index /FID ne {def} "
  794.       "{pop pop} ifelse} forall\n", fp);
  795.     fputs("   /Encoding BitEncoding def ", fp);
  796.     fputs(" currentdict end\n   definefont pop\n} bind def\n", fp);
  797. }
  798.  
  799. /****************************************************************
  800.  * Define commands to handle justification of a string placement
  801.  *****************************************************************/
  802.  
  803. struct tplace
  804. {
  805.     const char *pname;        /* placement name          */
  806.     const char *how;        /* how to do it(PS code)   */
  807. };
  808.  
  809. static const struct tplace pt[] =
  810. {
  811.     {"Lshow", "show"},
  812.     {"Cshow", "dup stringwidth pop -2 div 0 rmoveto show"},
  813.     {"Rshow", "dup stringwidth pop neg 0 rmoveto show"}
  814. };
  815.  
  816. static void
  817. PS_text_init(FILE * fp)
  818. {
  819.     if (number_of_text() <= 0)
  820.     return;
  821.  
  822.     fputs("% Specific to text\n", fp);
  823.     fprintf(fp, "/%s { %s } bind def\n", pt[0].pname, pt[0].how);
  824.     fprintf(fp, "/%s { %s } bind def\n", pt[1].pname, pt[1].how);
  825.     fprintf(fp, "/%s { %s } bind def\n", pt[2].pname, pt[2].how);
  826.     fputs("/SetFont {findfont exch scalefont setfont} bind def\n", fp);
  827.     if (!textscale)
  828.     fprintf(fp, "/fixed {xscale yscale add 2 div div} bind def\n");
  829.     switch_to_ISOLatin1(fp);
  830. }
  831.  
  832. /**************************************************************
  833.  * scale a font. Assuming fname is indeed a pointer to constant
  834.  ***************************************************************/
  835. #define MAXCFONT 60
  836.  
  837. static void
  838. PS_text_scale(FILE * fp, const char *fname, float size)
  839. {
  840.     static const char *fontcache[MAXCFONT], *cfont;
  841.     const char *suffix = "ISOLatin1";
  842.     static float csize;
  843.     int i;
  844.     float ssize;
  845.  
  846.     if (psnewfont)
  847.       {                /* should be turned on by ps_dump */
  848.       csize = -1;
  849.       cfont = 0;
  850.       psnewfont = 0;
  851.       for (i = MAXCFONT; --i >= 0;)
  852.           fontcache[i] = 0;
  853.       }
  854.  
  855.     /* change font only if different from current */
  856.  
  857.     if (Abs(size - csize) > 0.2 || cfont != fname)
  858.       {
  859.       int old;
  860.       /* define new font only once */
  861.       for (i = old = 0; !old && fontcache[i] && i < MAXCFONT; i++)
  862.           old = fname == fontcache[i];
  863.  
  864.       if (!old)
  865.         {            /* virgin */
  866.         fprintf(fp, "/%s%s /%s SwitchToISO\n", fname, suffix, fname);
  867.         i = -1;
  868.         while (fontcache[++i])
  869.             ;
  870.         fontcache[i] = fname;
  871.         }
  872.  
  873.       if (textscale)
  874.         {
  875.         ssize = size;
  876.         /* make sure scaled size is no less than MINTEXSIZE */
  877.         if ((ssize * 0.5 * s2p * (gxscale + gyscale)) < MINTEXTSIZE)
  878.             ssize = MINTEXTSIZE * 2.0 / (s2p * (gxscale + gyscale));
  879.         fprintf(fp, "%.2f /%s%s SetFont\n", ssize, fname, suffix);
  880.         }
  881.       else
  882.         {            /* do not want to scale text */
  883.         fprintf(fp, "%.2f fixed /%s%s SetFont\n", size, fname,
  884.             strcmp(fname, "Symbol") ? suffix : "");
  885.         }
  886.       cfont = fname;
  887.       csize = size;
  888.       }
  889. }
  890.  
  891. static void
  892. PS_color(FILE * fp, int r, int g, int b, int gray)
  893. {
  894.     static int cr, cg, cb;
  895.  
  896.     if (psnewcolor)
  897.       {
  898.       cr = cg = cb = -1;
  899.       psnewcolor = 0;
  900.       }
  901.  
  902.     if ((cr - r) || (cg - g) || (cb - b))
  903.       {                /* different */
  904.       if (gray)
  905.           fprintf(fp, "%.2f setgray ", C2NC(rgb2gray(r, g, b)));
  906.       else
  907.           fprintf(fp, "%.2f %.2f %.2f setrgbcolor ",
  908.               C2NC(r), C2NC(g), C2NC(b));
  909.       cr = r;
  910.       cg = g;
  911.       cb = b;
  912.       }
  913. }
  914.  
  915. /*********************************************************************
  916.  * output the string. Need to take care of unprintable and
  917.  * special characters
  918.  *******************************************************************/
  919.  
  920. static void
  921. PS_print_string(FILE * fp, const char *str)
  922. {
  923.     register const unsigned char *p = (const unsigned char *) str;
  924.  
  925.     while (p && *p)
  926.       {
  927.       if (*p < 32 || *p > 123)
  928.         {
  929.         fprintf(fp, "\\%o", *p);
  930.         }
  931.       else
  932.         {
  933.         if (PS_SPC(*p))
  934.             putc('\\', fp);
  935.         putc(*p, fp);
  936.         }
  937.       p++;
  938.       }
  939. }
  940.  
  941. /* given a font and a string, get the length */
  942. const char *pslen = "/strlength strlength (%s) stringwidth pop add def\n";
  943.  
  944. static void
  945. PS_out_text(FILE * fp, int n)
  946. {
  947.     register const char *p;
  948.     int pl = get_text_placement(n);
  949.     Line *line = get_text_line(n);
  950.     int nt = line->ntokens;
  951.     const char *cfont = get_text_fontname(n);
  952.     float csize = get_text_fontsize(n);
  953.  
  954.     p = get_text_string(n);
  955.  
  956.     fprintf(fp, "%% str%d %s\n", n, p);
  957.  
  958.     if (nt == 1)
  959.       {                /* not font switch */
  960.       PS_text_scale(fp, cfont, csize);
  961.       putc('(', fp);
  962.       PS_print_string(fp, p);
  963.       fprintf(fp, ") %s\n", pt[pl].pname);
  964.       }
  965.     else
  966.       {                /* need font switching */
  967.       Token *t, *ts;
  968.       if (pl == TCENTER || pl == TRIGHT)
  969.         {
  970.         fputs("/strlength 0 def\n", fp);
  971.         /* get the string length */
  972.         for (t = line->token, ts = t + nt; t < ts; t++)
  973.           {
  974.               PS_text_scale(fp, t->math ? "Symbol" : cfont, t->size);
  975.               fprintf(fp, pslen, t->str);
  976.           }
  977.         /* string length is now in strlength */
  978.         if (pl == TCENTER)
  979.             fputs("strlength -2 div 0 rmoveto\n", fp);
  980.         else
  981.             fputs("strlength neg 0 rmoveto\n", fp);
  982.         }
  983.  
  984.       /* now output the string */
  985.       for (t = line->token, ts = t + nt; t < ts; t++)
  986.         {
  987.         PS_text_scale(fp, t->math ? "Symbol" : cfont, t->size);
  988.         putc('(', fp);    /* ) */
  989.         PS_print_string(fp, t->str);
  990.         /* ( */
  991.         fprintf(fp, ") show\n");
  992.         }
  993.       }
  994. }
  995.  
  996. /*********************************************************************
  997.  *  Routine invoked to handle all text
  998.  **********************************************************************/
  999. static void
  1000. PS_text(IPTR im)
  1001. {
  1002.     int n, r, g, b;
  1003.     int px, py, i;
  1004.     float trot;
  1005.     FILE *fp = im->fp;
  1006.  
  1007.     if ((n = number_of_text()) <= 0)
  1008.     return;
  1009.  
  1010.     fputs("\n% Text. All distance are from ll corner\n", fp);
  1011.     for (i = 0; i < n; i++)
  1012.       {
  1013.       get_text_color(i, &r, &g, &b);
  1014.       PS_color(fp, r, g, b, IS_GRAY(im));
  1015.       get_text_location(i, &px, &py);
  1016.       PS_move(fp, px, py);
  1017.       if ((trot = get_text_rotation(i)) > 0.1)
  1018.         {
  1019.         PS_push(fp);
  1020.         fprintf(fp, "%.1f rotate\n", trot);
  1021.         PS_out_text(fp, i);
  1022.         PS_pop(fp);
  1023.         }
  1024.       else
  1025.           PS_out_text(fp, i);
  1026.       }
  1027. }
  1028.  
  1029. /********* END of text routines *******************}*********/
  1030.  
  1031. /************************************************************************
  1032.  * simple geometric figure drawings. could've written a PS routine that
  1033.  * takes (x,y), (w,h), t as parameters and produces the figure, but this
  1034.  * way is faster. The only problem is that when the scale in x&y direction
  1035.  * is very different, the line thickness would appear uneven .... could be
  1036.  * corrected by using more than one strokes .....
  1037.  *
  1038.  * Basic commands:
  1039.  * M -- moveto   LT --  lineto    L -- line from (xi,yi) to (xf,yf)
  1040.  *
  1041.  * Make sure number of objects not exceed MAXOBJDEFS. All PS objects have
  1042.  * a size of 2
  1043.  ***********************************************************************/
  1044. const char ps_plus[] = "-1 0 1 0 L 0 -1 0 1 L";
  1045. const char ps_rect[] = "-1 1 -1 -1 L 1 1 LT 1 -1 LT C";
  1046. const char ps_tri[] = " -1 -1 0 1 L 1 -1 LT C";
  1047. const char ps_circ[] = " 0 0 1 0 360 arc";
  1048. const char ps_hdist[] = "-1 0 1 0 L 1 0.25 1 -0.25 L -1 0.25 -1 -0.25 L";
  1049. const char ps_line[] = "0 1 0 -1 L";
  1050.  
  1051. const char ps_arrow[] = "0.7 0 -1 0 L dup setlinewidth stroke \n"
  1052. "0.7 0 M 0.7 -0.10 LT 1 0 LT 0.7 0.10 LT 0.7 0 LT C";
  1053.  
  1054. const char ps_star[] =
  1055. "0 1 0.2245 0.309 L -0.2245 0.309 LT -0.9511 0.309 LT\n"
  1056. "-0.3633 -0.118 LT -0.5878 -0.809 LT 0 -0.382 LT 0.5878 -0.809 LT\n"
  1057. "0.3633 -0.1180 LT 0.9511 0.309 LT 0.2245 0.309 LT C ";
  1058.  
  1059. static const char *pspaint[] =
  1060. {
  1061.     "stroke", "fill"
  1062. };
  1063.  
  1064. static void
  1065. PS_sgf_init(FILE * fp)
  1066. {
  1067.     const char *sgfi = "sgfi";
  1068.     const char *name;
  1069.     const char *nbuff[MAXOBJDEFS];
  1070.     int i, n, s, j, k = 0;
  1071.  
  1072.     /* sgf definitions */
  1073.     if ((n = number_of_sgf()) <= 0)
  1074.     return;
  1075.  
  1076.     for (j = MAXOBJDEFS; --j >= 0;)
  1077.     nbuff[j] = 0;
  1078.  
  1079.     fputs("% SGFs. All takes t, w, h,angle, xo, yo\n", fp);
  1080.     fputs("/M {moveto} bind def /LT {lineto} bind def\n", fp);
  1081.     fputs("/L {M LT}   bind def /C {closepath} bind def\n", fp);
  1082.     fputs("/Bgnsgf {gsave} def /Endsgf {grestore} def\n", fp);
  1083.     fprintf(fp, "/%s {newpath translate rotate scale} bind def\n", sgfi);
  1084.  
  1085.     /* define all the stuff used. */
  1086.     for (i = 0; i < n; i++)
  1087.       {
  1088.       name = sgf_name(i);
  1089.       for (j = s = 0; !s && j < MAXOBJDEFS; j++)
  1090.           s = (name == nbuff[j]);
  1091.  
  1092.       if (!s)
  1093.         {
  1094.         nbuff[k++] = name;
  1095.         fprintf(fp, "/%s {%s %s setlinewidth} def\n",
  1096.             sgf_name(i), sgfi, sgf_psdraw(i));
  1097.         }
  1098.       }
  1099. }
  1100.  
  1101. /* take care of simple geometric figures */
  1102. static void
  1103. PS_sgf(IPTR im)
  1104. {
  1105.     int i, n;
  1106.     int x, y, w, h, t, f, r, g, b;
  1107.     int angle;
  1108.     FILE *fp = im->fp;
  1109.  
  1110.     if ((n = number_of_sgf()) <= 0)
  1111.     return;
  1112.  
  1113.     fputs("\n% sgfs. Stack: t x y w h. Distance are from LL corner\n", fp);
  1114.     for (i = 0; i < n; i++)
  1115.       {
  1116.       get_sgf_info(i, &x, &y, &w, &h, &t, &f, &angle);
  1117.       get_sgf_color(i, &r, &g, &b);
  1118.       PS_color(fp, r, g, b, IS_GRAY(im));
  1119.  
  1120.       /*
  1121.        * all drawing routines have a size of 2 and thickness is scaled by
  1122.        * w or h, an average is taken
  1123.        */
  1124.       fprintf(fp, "Bgnsgf %.3g %.3g %g %d %d %d %s ",
  1125.           (float) 2.0 * t / (w + h), 0.5 * w, 0.5 * h, angle,
  1126.           x, y, sgf_name(i));
  1127.       /* color and stroke */
  1128.       fprintf(fp, "%s Endsgf\n", pspaint[f]);
  1129.       }
  1130.     return;
  1131. }
  1132.  
  1133. static int to_ps(IPTR);
  1134. static int to_cps(IPTR);
  1135.  
  1136. /**********************************************************************
  1137.  * write ps files. apply all transformation at the beginning of writing,
  1138.  * and * since all text and sgfs are relative to image, this reduces
  1139.  * ps activities
  1140.  ***********************************************************************/
  1141. int
  1142. PS_dump(IPTR im)
  1143. {
  1144.     int status;
  1145.  
  1146.     psnewcolor = psnewfont = 1;    /* signal cache re-init   */
  1147.     screen_dpi = get_scr_dpi();
  1148.     get_misc_size(im, &misct, &miscl, &miscb, &miscr);
  1149.     PS_header(im);
  1150.     PS_push(im->fp);        /* save old state          */
  1151.  
  1152.     /*
  1153.      * apply current transformation, particularly, the lower left corner of
  1154.      * the image will be the origin
  1155.      */
  1156.  
  1157.     PS_map(im);
  1158.  
  1159.     /* write out the raster */
  1160.     status = (IS_RGBA(im) ? to_cps : to_ps) (im);
  1161.  
  1162.     /* do text and sgfs */
  1163.     PS_text(im);
  1164.     PS_sgf(im);
  1165.  
  1166.     PS_pop(im->fp);        /* restore old state      */
  1167.     PS_trailer(im->fp);
  1168.     remove_progress_report();
  1169.     return status;
  1170. }
  1171.  
  1172. #define OUTPIXEL(c)                                           \
  1173.           do {                                                \
  1174.                putc(hexdigits[(c) >> 4],fp);                  \
  1175.                putc(hexdigits[(c) & 15],fp);                  \
  1176.           } while (ZERO)
  1177.  
  1178. static int
  1179. to_ps(IPTR im)
  1180. {
  1181.     register rgba_t *rgba, *rs;
  1182.     register int k, g, j;
  1183.     long rlines;
  1184.     FILE *fp = im->fp;
  1185.  
  1186.     PS_push(im->fp);        /* save old state  */
  1187.     PS_ras_scale(im);
  1188.     /* command that starts the image stream */
  1189.     fputs("% raster starts here\n", fp);
  1190.     fprintf(fp, "%s\n", pscmd);
  1191.  
  1192.     rlines = progress_report(IS_BW(im) ?
  1193.               "Writing B&W PS ..." : "Write GrayPS ...", im->h);
  1194.  
  1195.     for (j = 0, k = 1; j < im->h; j++)
  1196.       {
  1197.       rgba = ((rgba_t **) im->mraster)[im->h - 1 - j];
  1198.       for (rs = rgba + im->w; rgba < rs; rgba++, k++)
  1199.         {
  1200.         g = (*rgba & (PCMAX - 1));
  1201.         OUTPIXEL(g);
  1202.         if ((k % PS_LINE) == 0)
  1203.             putc('\n', fp);
  1204.         }
  1205.       REPORT(j, rlines);
  1206.       }
  1207.     putc('\n', im->fp);
  1208.     PS_pop(im->fp);        /* restore old state  */
  1209.     return 0;
  1210. }
  1211.  
  1212. /************** Color postscript *****************************/
  1213.  
  1214. static int
  1215. to_cps(IPTR im)
  1216. {
  1217.     int j, k;
  1218.     long rlines;
  1219.     FILE *fp = im->fp;
  1220.     register pc_t *pc, *ss;
  1221.  
  1222.     /* get the rgb matrix before we do anything */
  1223.     if (img_get_rgb(im) < 0)
  1224.     return -1;
  1225.  
  1226.     PS_push(im->fp);        /* save old state  */
  1227.     PS_ras_scale(im);
  1228.  
  1229.     /* command that starts the image stream */
  1230.  
  1231.     fputs("% raster starts here\n", fp);
  1232.     fprintf(fp, "%s\n", pscmd);
  1233.  
  1234.     rlines = progress_report("Write ColorPS ...", im->h);
  1235.  
  1236.     /* always code the default */
  1237.     for (j = im->h, k = 1; --j >= 0;)
  1238.       {
  1239.       for (pc = get_RM(im)[j], ss = pc + im->w; pc < ss; pc++, k++)
  1240.         {
  1241.         OUTPIXEL(*pc);
  1242.         if ((k % PS_LINE) == 0)
  1243.             putc('\n', fp);
  1244.         }
  1245.  
  1246.       for (pc = get_GM(im)[j], ss = pc + im->w; pc < ss; pc++, k++)
  1247.         {
  1248.         OUTPIXEL(*pc);
  1249.         if ((k % PS_LINE) == 0)
  1250.             putc('\n', fp);
  1251.         }
  1252.  
  1253.       for (pc = get_BM(im)[j], ss = pc + im->w; pc < ss; pc++, k++)
  1254.         {
  1255.         OUTPIXEL(*pc);
  1256.         if ((k % PS_LINE) == 0)
  1257.             putc('\n', fp);
  1258.         }
  1259.       REPORT((im->h - 1 - j), rlines);
  1260.       }
  1261.     img_free_rgbmem(im);
  1262.     putc('\n', im->fp);
  1263.     PS_pop(im->fp);
  1264.     return 0;
  1265. }
  1266.  
  1267. /************************************************************************
  1268.  * GUI part of the PostScript support and ultimately we want
  1269.  * paper size, x and/or y scale, placement and print resolution
  1270.  **********************************************************************/
  1271.  
  1272.  
  1273. /* tem copies */
  1274. static float lpw = PG_WIDTH, lph = PG_HEIGHT;
  1275. static float lhm = MARGIN, lvm = MARGIN;
  1276. static float lxscale = 1.0, lyscale = 1.0;
  1277. static int lautoscale = 1, ltextscale, lmatchscreen;
  1278. static int lplace = -1;
  1279. static int cpaper, defp;
  1280. static int ldpi = PRINTER_DPI;
  1281. static void reset_form(void);
  1282.  
  1283. static FL_OBJECT *xshow, *yshow;
  1284. static FL_OBJECT *sum;
  1285.  
  1286. /***************************************************************
  1287.  * Make a local copy of the control parameters for undo.
  1288.  * Need to use the control parameters for interactive reporting
  1289.  ***************************************************************/
  1290. static void
  1291. copy_real(void)
  1292. {
  1293.     lpw = page_w;
  1294.     lph = page_h;
  1295.     lxscale = gxscale;
  1296.     lyscale = gyscale;
  1297.     ldpi = pdpi;
  1298.     lautoscale = autoscale;
  1299.     ltextscale = textscale;
  1300.     lmatchscreen = matchscreen;
  1301.     lplace = place;
  1302.     cpaper = defp - 1;
  1303.     lhm = hm;
  1304.     lvm = vm;
  1305. }
  1306.  
  1307. static void
  1308. cancel_change(void)
  1309. {
  1310.     page_w = lpw;
  1311.     page_h = lph;
  1312.     gxscale = lxscale;
  1313.     gyscale = lyscale;
  1314.     autoscale = lautoscale;
  1315.     textscale = ltextscale;
  1316.     matchscreen = lmatchscreen;
  1317.     place = lplace;
  1318.     hm = lhm;
  1319.     vm = lvm;
  1320.     pdpi = ldpi;
  1321.     defp = cpaper + 1;
  1322. }
  1323.  
  1324. /*** Summarize current settings and (possibly) results *****/
  1325.  
  1326. static void
  1327. get_summary(void)
  1328. {
  1329.     int w = imgptr->w, h = imgptr->h;
  1330.     float winch, hinch;
  1331.     float pw = page_w - 2 * hm;
  1332.     float ph = page_h - 2 * vm;
  1333.     char tmp[80];
  1334.  
  1335.     check_size(&w, &h, 0);
  1336.  
  1337.     /* resulting hardcopy size in inches  */
  1338.     winch = (w + (miscl + miscr) * s2p * gxscale) / 72.0;
  1339.     hinch = (h + (misct + miscb) * s2p * gyscale) / 72.0;
  1340.  
  1341.     if (!landscape)
  1342.     sprintf(tmp, "printed size %.2g X %.2g inch", winch, hinch);
  1343.     else
  1344.     sprintf(tmp, "printed size %.2g X %.2g inch", hinch, winch);
  1345.  
  1346.     /* if too large, change size to red */
  1347.     if ((landscape && (hinch > pw || winch > ph)) ||
  1348.     (!landscape && (winch > pw || hinch > ph)))
  1349.     fl_set_object_lcol(sum, FL_RED);
  1350.     else
  1351.     fl_set_object_lcol(sum, FL_BLACK);
  1352.  
  1353.     fl_set_object_label(sum, tmp);
  1354. }
  1355.  
  1356. /********* cancel. copy the true one to local one *******/
  1357.  
  1358. /* ARGSUSED */
  1359. static void
  1360. ok_cancel(FL_OBJECT * p, long ok)
  1361. {
  1362.     if (ok)
  1363.       {
  1364.       copy_real();
  1365.       update_dumpinfo(PS_wdefault(0));
  1366.       }
  1367.     else
  1368.       {
  1369.       cancel_change();
  1370.       reset_form();
  1371.       }
  1372.     bit_hide_form(fmpsinit);
  1373.     fl_activate_all_forms();
  1374. }
  1375.  
  1376.  
  1377. /* handle xscale */
  1378. #define MINPSSCALE    0.05    /* minimum scale */
  1379. #define MAXPSSCALE    5.0    /* maximum scale */
  1380. #define MINPSSTEP     0.01    /* change rate   */
  1381. #define MAXPSSTEP     0.10    /* change rate   */
  1382.  
  1383. /* ARGSUSED */
  1384. static void
  1385. xscale_cb(FL_OBJECT * p, long q)
  1386. {
  1387.     const char *rep;
  1388.  
  1389.     if (autoscale)
  1390.     return;
  1391.  
  1392.     if (q == 4)
  1393.       {                /* small decrease */
  1394.       gxscale -= MINPSSTEP;
  1395.       }
  1396.     else if (q == 44)
  1397.       {                /* large decrease */
  1398.       gxscale -= MAXPSSTEP;
  1399.       }
  1400.     else if (q == 6)
  1401.       {                /* small increase */
  1402.       gxscale += MINPSSTEP;
  1403.       }
  1404.     else if (q == 66)
  1405.       {                /* large increase  */
  1406.       gxscale += MAXPSSTEP;
  1407.       }
  1408.  
  1409.     if (gxscale < MINPSSCALE)
  1410.     gxscale = MINPSSCALE;
  1411.     if (gxscale > MAXPSSCALE)
  1412.     gxscale = MAXPSSCALE;
  1413.  
  1414.     gyscale = gxscale;
  1415.     get_summary();
  1416.     rep = ftoa(gxscale, 2);
  1417.     fl_set_object_label(xshow, rep);
  1418.     fl_set_object_label(yshow, rep);
  1419. }
  1420.  
  1421. /* ARGSUSED */
  1422. static void
  1423. yscale_cb(FL_OBJECT * p, long q)
  1424. {
  1425.     if (q == 4)
  1426.       {                /* small decrease */
  1427.       gyscale -= MINPSSTEP;
  1428.       }
  1429.     else if (q == 44)
  1430.       {                /* large decrease */
  1431.       gyscale -= MAXPSSTEP;
  1432.       }
  1433.     else if (q == 6)
  1434.       {                /* small increase */
  1435.       gyscale += MINPSSTEP;
  1436.       }
  1437.     else if (q == 66)
  1438.       {                /* large increase  */
  1439.       gyscale += MAXPSSTEP;
  1440.       }
  1441.     if (gyscale < MINPSSCALE)
  1442.     gyscale = MINPSSCALE;
  1443.     if (gyscale > MAXPSSCALE)
  1444.     gyscale = MAXPSSCALE;
  1445.  
  1446.     fl_set_object_label(yshow, ftoa(gyscale, 2));
  1447.     get_summary();
  1448. }
  1449.  
  1450. /* ARGSUSED */
  1451. static void
  1452. placement_cb(FL_OBJECT * p, long q)
  1453. {
  1454.     place = q;
  1455.     get_summary();
  1456. }
  1457.  
  1458. /* ARGSUSED */
  1459. static void
  1460. autoscale_cb(FL_OBJECT * p, long q)
  1461. /* preferably, all scale changing actions be disabled */
  1462. {
  1463.     if ((autoscale = fl_get_button(p)))
  1464.       {
  1465.       fl_set_object_lcol(xshow, FL_INACTIVE);
  1466.       fl_set_object_lcol(yshow, FL_INACTIVE);
  1467.       }
  1468.     else
  1469.       {
  1470.       fl_set_object_lcol(xshow, 0);
  1471.       fl_set_object_lcol(yshow, 0);
  1472.       }
  1473.     get_summary();
  1474.     fl_set_object_label(xshow, ftoa(gxscale, 2));
  1475.     fl_set_object_label(yshow, ftoa(gyscale, 2));
  1476. }
  1477.  
  1478. /* ARGSUSED */
  1479. static void
  1480. textscale_cb(FL_OBJECT * p, long q)
  1481. {
  1482.     textscale = fl_get_button(p);
  1483. }
  1484.  
  1485. /* ARGSUSED */
  1486. static void
  1487. matchscreen_cb(FL_OBJECT * p, long q)
  1488. {
  1489.     matchscreen = fl_get_button(p);
  1490.     get_summary();
  1491. }
  1492.  
  1493. struct papers
  1494. {
  1495.     const char *name;
  1496.     float w, h, m;
  1497. };
  1498.  
  1499. static struct papers allpapers[] =
  1500. {
  1501.     {"Letter (8.5X11.0in)", 8.5, 11.0, MARGIN},    /* A US letter */
  1502.     {"Legal (8.5X14.0in)", 8.5, 14.0, MARGIN},
  1503.     {"A4 (210X295mm)", 7.27, 11.61, MARGIN},    /* European */
  1504.     {"B4 (257X364mm)", 10.1, 14.33, MARGIN},    /* Japanese */
  1505.     {"B5 (18X20cm)", 7.283, 10.630, MARGIN},
  1506.     {"B (11X17in)", 11.0, 17.0, MARGIN},    /* US tabloid */
  1507.     {"Note (4X5in)", 4.0, 5.0, 0.2}
  1508. };
  1509.  
  1510. static void
  1511. set_paper(int n)
  1512. {
  1513.     page_w = allpapers[n].w;
  1514.     page_h = allpapers[n].h;
  1515.     hm = vm = allpapers[n].m;
  1516.     get_summary();
  1517. }
  1518.  
  1519. /* ARGSUSED */
  1520. static void
  1521. paper_size(FL_OBJECT * p, long q)
  1522. {
  1523.     int i = fl_get_choice(p);
  1524.     if (i <= 0)
  1525.     return;
  1526.     cpaper = i - 1;
  1527.     set_paper(cpaper);
  1528. }
  1529.  
  1530. /* ARGSUSED */
  1531. static int alldpi[] =
  1532. {
  1533.     75, 150, 300, 400, 600, 1200, 0
  1534. };
  1535.  
  1536. static void
  1537. printer_cb(FL_OBJECT * p, long q)
  1538. {
  1539.     int i = fl_get_choice(p);
  1540.     static int j = PRINTER_DPI;
  1541.     char buf[30];
  1542.  
  1543.     if (i <= 0)
  1544.     return;
  1545.  
  1546.     if (i == sizeof(alldpi) / sizeof(alldpi[0]))
  1547.       {
  1548.       getint("EnterDPI", &j, 35, 3600, 0);
  1549.       sprintf(buf, "%d or other", j);
  1550.       fl_replace_choice(p, i, buf);
  1551.       alldpi[i - 1] = j;
  1552.       }
  1553.     pdpi = alldpi[i - 1];
  1554.     get_summary();
  1555. }
  1556.  
  1557. static FL_OBJECT *as, *ts, *sc, *ms;
  1558.  
  1559. static void
  1560. reset_form(void)
  1561. {
  1562.     fl_set_button(as, autoscale);
  1563.     fl_set_button(ts, textscale);
  1564.     fl_set_button(ms, matchscreen);
  1565.     fl_set_choice(sc, defp);
  1566.     get_summary();
  1567. }
  1568.  
  1569. static void
  1570. create_form_psinit(void)
  1571. {
  1572.     struct papers *ps, *p = allpapers;
  1573.     char cc[30];
  1574.     FL_OBJECT *obj;
  1575.     int i;
  1576.     float x, y, dx, dy;
  1577.  
  1578.     if (fmpsinit)
  1579.     return;
  1580.  
  1581.     fmpsinit = fl_bgn_form(FL_NO_BOX, 255.0, 275.0);
  1582.     obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 255.0, 275.0, "");
  1583.     obj = fl_add_box(FL_NO_BOX, 30.0, 245.0, 200.0, 20.0, "PostScript Options");
  1584.     fl_set_object_lcol(obj, 4);
  1585.     fl_set_object_lstyle(obj, FL_BOLD_STYLE);
  1586.     obj = fl_add_button(FL_HB, 0.0, 0.0, 255.0, 275.0, "");
  1587.     fl_set_call_back(obj, help_cb, HELP_PS);
  1588.  
  1589.     /* current results */
  1590.     sum = obj = fl_add_text(FL_NT, 40.0, 215.0, 180.0, 20.0, "");
  1591.     fl_set_object_boxtype(obj, FL_ROUNDED_BOX);
  1592.     fl_set_object_lsize(obj, 10.0);
  1593.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  1594.  
  1595.     /* placement */
  1596.     fl_bgn_group();
  1597.     obj = fl_add_roundbutton(FL_RB, 10.0, 180.0, 30.0, 30.0, rplace[1]);
  1598.     fl_set_object_lsize(obj, 10.0);
  1599.     fl_set_button(obj, place == 0);
  1600.     fl_set_call_back(obj, placement_cb, 0);
  1601.     obj = fl_add_roundbutton(FL_RB, 90.0, 180.0, 30.0, 30.0, rplace[2]);
  1602.     fl_set_object_lsize(obj, 10.0);
  1603.     fl_set_button(obj, place == 1);
  1604.     fl_set_call_back(obj, placement_cb, 1);
  1605.     obj = fl_add_roundbutton(FL_RB, 180.0, 180.0, 30.0, 30.0, rplace[0]);
  1606.     fl_set_object_lsize(obj, 10.0);
  1607.     fl_set_button(obj, place == -1);
  1608.     fl_set_call_back(obj, placement_cb, -1);
  1609.     fl_end_group();
  1610.  
  1611.     /* paper size */
  1612.     sc = obj = fl_add_choice(FL_NC, 65.0, 130.0, 140.0, 25.0, "Paper");
  1613.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1614.     fl_set_object_lcol(obj, 4);
  1615.     fl_set_object_lsize(obj, 10.0);
  1616.     fl_set_choice_fontsize(obj, 10.0);
  1617.     fl_clear_choice(obj);
  1618.  
  1619.     for (ps = p + sizeof(allpapers) / sizeof(allpapers[0]); p < ps; p++)
  1620.       {
  1621.       fl_addto_choice(obj, p->name);
  1622.       if (Abs(p->w - page_w) < 0.1 && Abs(p->h - page_h) < 0.1)
  1623.           defp = p - allpapers + 1;
  1624.       }
  1625.  
  1626.     if (defp <= 0)
  1627.     defp = 1;
  1628.  
  1629.     cpaper = defp - 1;
  1630.     set_paper(cpaper);
  1631.     fl_set_choice(obj, defp);
  1632.     fl_set_call_back(obj, paper_size, 0);
  1633.  
  1634.     /* printer resolutions */
  1635.     obj = fl_add_choice(FL_NC, 65.0, 155.0, 140.0, 25.0, "Printer");
  1636.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1637.     fl_set_object_lcol(obj, 4);
  1638.     fl_set_object_lsize(obj, 10.0);
  1639.     fl_set_choice_fontsize(obj, 10.0);
  1640.     fl_clear_choice(obj);
  1641.  
  1642.     for (i = 0; i < sizeof(alldpi) / sizeof(alldpi[0]); i++)
  1643.       {
  1644.       if (alldpi[i] == 0)
  1645.           fl_addto_choice(obj, "Other");
  1646.       else
  1647.         {
  1648.         sprintf(cc, "%d DPI", alldpi[i]);
  1649.         fl_addto_choice(obj, cc);
  1650.         }
  1651.       if (alldpi[i] == pdpi)
  1652.           fl_set_choice(obj, i + 1);
  1653.       }
  1654.     fl_set_call_back(obj, printer_cb, 0);
  1655.  
  1656.     /* scales */
  1657.     xshow = obj = fl_add_box(FL_FRAME_BOX, 115.0, 100.0,
  1658.                  40.0, 25.0, (char *) ftoa(gxscale, 2));
  1659.  
  1660.     fl_set_object_lsize(obj, 10.0);
  1661.     obj = fl_add_text(FL_NT, 10.0, 100.0, 55.0, 25.0, "xscale");
  1662.     fl_set_object_lcol(obj, 4);
  1663.     fl_set_object_lsize(obj, 10.0);
  1664.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  1665.     obj = fl_add_button(FL_TOUCH_BUTTON, 180.0, 100.0, 25.0, 25.0, "@>>");
  1666.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1667.     fl_set_object_color(obj, 47, 1);
  1668.     fl_set_object_lcol(obj, 1);
  1669.     fl_set_call_back(obj, xscale_cb, 66);
  1670.     obj = fl_add_button(FL_TOUCH_BUTTON, 65.0, 100.0, 25.0, 25.0, "@<<");
  1671.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1672.     fl_set_object_color(obj, 47, 1);
  1673.     fl_set_object_lcol(obj, 1);
  1674.     fl_set_call_back(obj, xscale_cb, 44);
  1675.     obj = fl_add_button(FL_TOUCH_BUTTON, 155.0, 100.0, 25.0, 25.0, "@>");
  1676.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1677.     fl_set_object_color(obj, 47, 1);
  1678.     fl_set_object_lcol(obj, 1);
  1679.     fl_set_call_back(obj, xscale_cb, 6);
  1680.     obj = fl_add_button(FL_TOUCH_BUTTON, 90.0, 100.0, 25.0, 25.0, "@<");
  1681.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1682.     fl_set_object_color(obj, 47, 1);
  1683.     fl_set_object_lcol(obj, 1);
  1684.     fl_set_call_back(obj, xscale_cb, 4);
  1685.  
  1686.     yshow = obj = fl_add_box(FL_FRAME_BOX, 115.0, 75.0,
  1687.                  40.0, 25.0, (char *) ftoa(gyscale, 2));
  1688.     fl_set_object_lsize(obj, 10.0);
  1689.     obj = fl_add_text(FL_NT, 10.0, 75.0, 55.0, 25.0, "yscale");
  1690.     fl_set_object_lcol(obj, 4);
  1691.     fl_set_object_lsize(obj, 10.0);
  1692.     fl_set_object_align(obj, FL_ALIGN_CENTER);
  1693.     obj = fl_add_button(FL_TOUCH_BUTTON, 180.0, 75.0, 25.0, 25.0, "@>>");
  1694.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1695.     fl_set_object_color(obj, 47, 1);
  1696.     fl_set_object_lcol(obj, 1);
  1697.     fl_set_call_back(obj, yscale_cb, 66);
  1698.     obj = fl_add_button(FL_TOUCH_BUTTON, 155.0, 75.0, 25.0, 25.0, "@>");
  1699.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1700.     fl_set_object_color(obj, 47, 1);
  1701.     fl_set_object_lcol(obj, 1);
  1702.     fl_set_call_back(obj, yscale_cb, 6);
  1703.     obj = fl_add_button(FL_TOUCH_BUTTON, 90.0, 75.0, 25.0, 25.0, "@<");
  1704.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1705.     fl_set_object_color(obj, 47, 1);
  1706.     fl_set_object_lcol(obj, 1);
  1707.     fl_set_call_back(obj, yscale_cb, 4);
  1708.     obj = fl_add_button(FL_TOUCH_BUTTON, 65.0, 75.0, 25.0, 25.0, "@<<");
  1709.     fl_set_object_boxtype(obj, FL_FRAME_BOX);
  1710.     fl_set_object_color(obj, 47, 1);
  1711.     fl_set_object_lcol(obj, 1);
  1712.     fl_set_call_back(obj, yscale_cb, 44);
  1713.  
  1714.  
  1715.     /* text, autoscale and match screen */
  1716.     dx = dy = 28.0;
  1717.     x = 10.0;
  1718.     y = 40.0;
  1719.     as = obj = fl_add_roundbutton(FL_PB, x, y, dx, dy, "autoscale");
  1720.     fl_set_object_lsize(obj, 10.0);
  1721.     fl_set_button(obj, autoscale);
  1722.     fl_set_call_back(obj, autoscale_cb, 0);
  1723.     autoscale_cb(obj, 0);
  1724.     x += dx + 50;
  1725.  
  1726.     ts = obj = fl_add_roundbutton(FL_PB, x, y, dx, dy, "textscale");
  1727.     fl_set_object_lsize(obj, 10.0);
  1728.     fl_set_button(obj, textscale);
  1729.     fl_set_call_back(obj, textscale_cb, 0);
  1730.     x += dx + 50;
  1731.  
  1732.     ms = obj = fl_add_roundbutton(FL_PB, x, y, dx, dy, "scrnsize");
  1733.     fl_set_object_lsize(obj, 10.0);
  1734.     fl_set_button(obj, matchscreen);
  1735.     fl_set_call_back(obj, matchscreen_cb, 0);
  1736.  
  1737.     /* control */
  1738.     obj = fl_add_button(FL_RETB, 150.0, 10.0, 75.0, 25.0, "OK");
  1739.     fl_set_object_color(obj, 47, 2);
  1740.     fl_set_object_lsize(obj, 10.0);
  1741.     fl_set_call_back(obj, ok_cancel, 1);
  1742.     obj = fl_add_button(FL_NB, 70.0, 10.0, 75.0, 25.0, "Cancel");
  1743.     fl_set_object_color(obj, 47, 3);
  1744.     fl_set_object_lsize(obj, 10.0);
  1745.     fl_set_call_back(obj, ok_cancel, 0);
  1746.     fl_end_form();
  1747.  
  1748.     /* initilize GUI local paramters */
  1749.     copy_real();
  1750. }
  1751.  
  1752. #endif /* !defined PS * */
  1753.  
  1754. /****************************************************************
  1755.  * get the sizes of text and sgfs that are outside of the image
  1756.  ******************************************************************/
  1757.  
  1758. void
  1759. get_misc_size(IPTR im, int *top, int *left, int *bottom, int *right)
  1760. {
  1761.     int l, ll, t, tt, r, rr, b, bb;
  1762.  
  1763.     get_text_bounds(im, &t, &l, &b, &r);
  1764.     get_sgf_bounds(im, &tt, &ll, &bb, &rr);
  1765.  
  1766.     *top = Max(t, tt);
  1767.     *left = Max(l, ll);
  1768.     *bottom = Max(b, bb);
  1769.     *right = Max(r, rr);
  1770.  
  1771.     if (screen_dpi == 0)
  1772.     screen_dpi = get_scr_dpi();
  1773.  
  1774.     *top = (*top * 72.0 / screen_dpi);
  1775.     *left = (*left * 72.0 / screen_dpi);
  1776.     *bottom = (*bottom * 72.0 / screen_dpi);
  1777.     *right = (*right * 72.0 / screen_dpi);
  1778. }
  1779.